﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Diagnostics;

using JavaSubstitutes;

namespace Sprites
{
	public class LevelRenderer
	{
		private int xCamera;
		private int yCamera;
		private Canvas canvas;
		private WriteableBitmap image;
		private static Color transparent = Color.FromArgb(0, 0, 0, 0);
		public Level level; //todo:made public for testing
		protected List<Image> LevelImages = new List<Image>();

		public bool renderBehaviors = false;

		public int Width;
		public int Height;

		public LevelRenderer(Level level, GraphicsConfiguration graphicsConfiguration, int width, int height)
		{
			this.Width = width;
			this.Height = height;

			this.level = level;
			image = graphicsConfiguration.createCompatibleImage(width, height, Transparency.BITMASK);
			canvas = new Canvas() { Width = width, Height = height };
			//updateArea(image, 0, 0, width, height);
			updateAreaViaCanvas(image, 0, 0, Width, Height);
		}

		public void setCam(WriteableBitmap wbmp, int xCam, int yCam)
		{
			//MELIOT: NOTE NOTE NOTE
			//wrt the scrolling problem- if this returns HERE, 
			//then the GROUND doesn't move- this renders the actual level (ground).
			//It also is pretty close to correct wrt collisions with hills, etc, until it scrolls- so this appears to scroll at the wrong rate wrt mario
			//that might explain why it lets mario off the screen!
			//Also see the caller: levelScene.tick
			//return;

			int xCamD = xCamera - xCam;
			int yCamD = yCamera - yCam;
			xCamera = xCam;
			yCamera = yCam;

			//g.setComposite(AlphaComposite.Src);
			//g.copyArea(0, 0, width, height, xCamD, yCamD);
			//wbmp.copyAndMoveArea(0, 0, Width, Height, xCamD, yCamD); 
			//meliot: is this required?

			if (xCamD < 0)
			{
				if (xCamD < -Width) xCamD = -Width;
				//updateArea(wbmp, Width + xCamD, 0, -xCamD, Height);
			}
			else if (xCamD > 0)
			{
				if (xCamD > Width) xCamD = Width;
				//updateArea(wbmp, 0, 0, xCamD, Height);
			}

			if (yCamD < 0)
			{
				if (yCamD < -Width) yCamD = -Width;
				//updateArea(wbmp, 0, Height + yCamD, Width, -yCamD);
			}
			else if (yCamD > 0)
			{
				if (yCamD > Width) yCamD = Width;
				//updateArea(wbmp, 0, 0, Width, yCamD);
			}
		}


		//consider:  is this essentially the same as the updateArea method for bgRenderer? couldn't we borrow from there
		//update area basically paints the ground
		public void updateAreaViaCanvas(WriteableBitmap wbmp, int x0, int y0, int w, int h)
		{
			//wbmp.Clear();
			//foreach (Image i in canvas.Children) LevelImages.Add(i);
			canvas.Children.Clear();

			//xCamera -= 48; //this makes the collision detection with the level occur where it's supposed to be if mario's small and the level hasn't scrolled... indicating that setCam is wrong

			int xTileStart = (x0 + xCamera) / 16;
			int yTileStart = (y0 + yCamera) / 16;
			int xTileEnd = (x0 + xCamera + w) / 16;
			int yTileEnd = (y0 + yCamera + h) / 16;
		
			for (int x = xTileStart; x <= xTileEnd; x++)
			{
				for (int y = yTileStart; y <= yTileEnd; y++)
				{
					int b = level.getBlock(x, y) & 0xff;
					if (((Level.TILE_BEHAVIORS[b]) & Level.BIT_ANIMATED) == 0)
					{
						Image img = new Image() { Source = new BitmapImage(((BitmapImage)Art.level[b % 16, b / 16].Source).UriSource) };
						Canvas.SetLeft(img, (x << 4) - xCamera);
						Canvas.SetTop(img, (y << 4) - yCamera);
						canvas.Children.Add(img);
					}
				}
			}
		}

		//#region Orig
		//private void updateArea(WriteableBitmap wbmp, int x0, int y0, int w, int h)
		//{
		//    wbmp.clearRect(x0, y0, w, h);

		//    int xTileStart = (x0 + xCamera) / 16;
		//    int yTileStart = (y0 + yCamera) / 16;
		//    int xTileEnd = (x0 + xCamera + w) / 16;
		//    int yTileEnd = (y0 + yCamera + h) / 16;
		//    for (int x = xTileStart; x <= xTileEnd; x++)
		//    {
		//        for (int y = yTileStart; y <= yTileEnd; y++)
		//        {
		//            int b = level.getBlock(x, y) & 0xff;
		//            if (((Level.TILE_BEHAVIORS[b]) & Level.BIT_ANIMATED) == 0)
		//            {
		//                wbmp.drawImage(Art.level[b % 16, b / 16], (x << 4) - xCamera, (y << 4) - yCamera);
		//            }
		//        }
		//    }
		//}
		//#endregion

		public void render(WriteableBitmap wbmp, int tick, double alpha)
		{
			//canvas.Children.Clear();
			//wbmp.drawImage(image	, 0, 0); //orig

			//update area basically paints the ground
			//updateArea(wbmp, 0, 0, Width, Height);
			updateAreaViaCanvas(wbmp, 0, 0, Width, Height);

			//this loop basically paints the stuff overhead like the blocks and coins
			for (int x = xCamera / 16; x <= (xCamera + Width) / 16; x++)
				for (int y = yCamera / 16; y <= (yCamera + Height) / 16; y++)
				{
					byte b = level.getBlock(x, y);

					if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_ANIMATED) == 0) continue;
					
					int animTime = (tick / 3) % 4;

					if ((b % 16) / 4 == 0 && b / 16 == 1)
					{
						animTime = (tick / 2 + (x + y) / 8) % 20;
						if (animTime > 3) animTime = 0;
					}
					if ((b % 16) / 4 == 3 && b / 16 == 0)
					{
						animTime = 2;
					}
					int yo = 0;

					if (x >= 0 && y >= 0 && x < level.width && y < level.height) yo = level.data[x, y];
					if (yo > 0) yo = (int)(Math.Sin((yo - alpha) / 4.0f * Math.PI) * 8);

					Image img = new Image() { Source = new BitmapImage(((BitmapImage)Art.level[(b % 16) / 4 * 4 + animTime, b / 16].Source).UriSource) };
					Canvas.SetLeft(img, (x << 4) - xCamera);
					Canvas.SetTop(img, (y << 4) - yCamera - yo);
					Canvas.SetZIndex(img, 2);
					canvas.Children.Add(img);


					#region pointless code?
					//meliot- what's the point of making red blocks? it doesn't seem to be used anywhere else
					//if (renderBehaviors)
					//{
					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_BLOCK_UPPER) > 0)
					//    {
					//        wbmp.fillRect((x << 4) - xCamera, (y << 4) - yCamera, 16, 2, Colors.Red);
					//    }

					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_BLOCK_ALL) > 0)
					//    {
					//        wbmp.fillRect((x << 4) - xCamera, (y << 4) - yCamera, 16, 2, Colors.Red);
					//        wbmp.fillRect((x << 4) - xCamera, (y << 4) - yCamera + 14, 16, 2, Colors.Red);
					//        wbmp.fillRect((x << 4) - xCamera, (y << 4) - yCamera, 2, 16, Colors.Red);
					//        wbmp.fillRect((x << 4) - xCamera + 14, (y << 4) - yCamera, 2, 16, Colors.Red);
					//    }

					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_BLOCK_LOWER) > 0)
					//    {
					//        wbmp.fillRect((x << 4) - xCamera, (y << 4) - yCamera + 14, 16, 2, Colors.Red);
					//    }

					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_SPECIAL) > 0)
					//    {
					//        wbmp.fillRect((x << 4) - xCamera + 2 + 4, (y << 4) - yCamera + 2 + 4, 4, 4, Colors.Red);
					//    }

					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_BUMPABLE) > 0)
					//    {
					//        wbmp.fillRect((x << 4) - xCamera + 2, (y << 4) - yCamera + 2, 4, 4, Colors.Blue);
					//    }

					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_BREAKABLE) > 0)
					//    {
					//        wbmp.fillRect((x << 4) - xCamera + 2 + 4, (y << 4) - yCamera + 2, 4, 4, Colors.Green);
					//    }

					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_PICKUPABLE) > 0)
					//    {
					//        wbmp.fillRect((x << 4) - xCamera + 2, (y << 4) - yCamera + 2 + 4, 4, 4, Colors.Yellow);
					//    }

					//    if (((Level.TILE_BEHAVIORS[b & 0xff]) & Level.BIT_ANIMATED) > 0)
					//    {
					//        //meliot- ? was this in the original code?
					//    }
					//}

					#endregion
				}

			wbmp.Render(canvas, null);
		}

		//public void repaint(WriteableBitmap wbmp, int x, int y, int w, int h)
		//{
		//    updateArea(wbmp, x * 16 - xCamera, y * 16 - yCamera, w * 16, h * 16);
		//}

		public void setLevel(WriteableBitmap wbmp, Level level)
		{
			this.level = level;
			updateAreaViaCanvas(wbmp, 0, 0, Width, Height);
		}

		public void renderExit0(WriteableBitmap wbmp, int tick, double alpha, bool bar)
		{
			for (int y = level.yExit - 8; y < level.yExit; y++)
			{
				wbmp.drawImage(Art.level[12, y == level.yExit - 8 ? 4 : 5], (level.xExit << 4) - xCamera - 16, (y << 4) - yCamera);
			}
			int yh = level.yExit * 16 - (int)((Math.Sin((tick + alpha) / 20) * 0.5 + 0.5) * 7 * 16) - 8;
			if (bar)
			{
				wbmp.drawImage(Art.level[12, 3], (level.xExit << 4) - xCamera - 16, yh - yCamera);
				wbmp.drawImage(Art.level[13, 3], (level.xExit << 4) - xCamera, yh - yCamera);
			}
		}

		public void renderExit1(WriteableBitmap wbmp, int tick, double alpha)
		{
			for (int y = level.yExit - 8; y < level.yExit; y++)
			{
				wbmp.drawImage(Art.level[13, y == level.yExit - 8 ? 4 : 5], (level.xExit << 4) - xCamera + 16, (y << 4) - yCamera);
			}
		}
	}
}
